/* (c) 2008,2009 Jonathan Hogg and Andreas Grothey, University of Edinburgh
 *
 * This file is part of SML.
 *
 * SML is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, using version 3 of the License.
 *
 * SML is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
 */

#include <iostream>
#include <fstream>
#include <cstring> // for strcmp() and strncmp()
#include <cstdlib> // for exit()
#include "mpi.h"
#include "Sml.h"
#include "backend.h"
#include "sml-oops.h"
#include "util.h"
#include "GlobalVariables.h"

using namespace std;

const string progname = "smloops";

/* global variables */
int GlobalVariables::rank = 0;
int GlobalVariables::size = 0;
string GlobalVariables::modelfilename = "";
string GlobalVariables::datafilename = "";
string GlobalVariables::amplcommand = "";
int GlobalVariables::degreeParallel;
bool GlobalVariables::logParseModel = true;
bool GlobalVariables::debug = false;

void writeHelp(ostream& out, const string& programname) {
   out << "Syntax:" << endl;
   out << "   " << programname
       << " [OPTIONS] modelfile datafile\n\n";
   out << "Option summary:" << endl;
   out << " -d                  Enables debug information when reading model "
      "file." << endl;
   out << " --help              Displays this help information." << endl;
   out << " --output=outfile," << endl;
   out << "   -o outfile        Write solution to file outfile." << endl;
   out << " modelfile           File containing SML model." << endl;
   out << " datafile            File containing SML data." << endl;
}

int analyseOptions(int argc, char **argv, string &modelfilename,
                   string &datafilename, string &outfilename,string &amplCommand,int &degreeParallel,bool &debug) {
	int found = 0;
	for (int i = 1; i < argc; i++)
	{
		if (strcmp(argv[i], "-d") == 0)
		{
			debug = true;
		}
		else if (strcmp(argv[i], "--help") == 0)
		{
			writeHelp(cout, progname);
			exit(0);
		}
		else if (strcmp(argv[i], "-o") == 0)
		{
			if (i + 1 == argc)
			{
				cerr << "-o supplied without filename" << endl;
				return 1;
			}
			outfilename = argv[++i];
			if (outfilename.at(0) == '-')
			{
				cerr << "-o supplied without filename" << endl;
				return 1;
			}
		}
		else if (strncmp(argv[i], "--output=", 9) == 0)
		{
			outfilename = (argv[i] + 9);
		}
		else if(strncmp(argv[i], "--with-ampl=",12)==0)
		{
			amplCommand = (argv[i] + 12);
		}
		else if(strcmp(argv[i],"-p")==0)
		{
			 degreeParallel = atoi(argv[++i]);
		}
		//======================================================
		else if (*(argv[i]) == '-')
		{
			cerr << "Unrecognised option '" << argv[i] << "'" << endl;
			return 1;
		}
		else
		{
			if (found == 0) {
				// first proper argument is the model file to read
				modelfilename = argv[i];
				found++;
			} else if (found == 1) {
				// next one is data file
				datafilename = argv[i];
				found++;
			} else {
				cerr << "ERROR: too many filenames." << endl;
				return 1;
			}
		}
	}

	if (modelfilename == "" || datafilename == "") {
		cerr << "ERROR: both modelfile and datafile "
			"must be supplied." << endl << endl;
		return 1;
	}
	return 0;
}

void initGloablVariables(int rank,int size,const string& modelfilename,const string& datafilename,const string &amplCommand,int degreeParallel,bool debug)
{
	GlobalVariables::rank = rank;
	GlobalVariables::size = size;
	GlobalVariables::logParseModel = debug;
	GlobalVariables::debug = debug;
	GlobalVariables::datafilename = datafilename;
	GlobalVariables::modelfilename = modelfilename;
	GlobalVariables::degreeParallel = degreeParallel;
	GlobalVariables::amplcommand = amplCommand;

	LOG("Rank ["<<rank<<"]");
	LOG("Size ["<<size<<"]");
	LOG("Debug["<<debug<<"]");
	LOG("Reading data file '" << datafilename << "'...");
	LOG("Reading model file '" << modelfilename << "'...");
	LOG("Degree of Parallel["<<degreeParallel<<"]");
	LOG("AmplCommand: "<<amplCommand);
}

/* ----------------------------------------------------------------------------
main
---------------------------------------------------------------------------- */
int main(int argc, char **argv)
{
	MPI::Init();
	MPI::Intercomm comm;
	comm = MPI::COMM_WORLD;
	int rank = comm.Get_rank();
	int size = comm.Get_size();
	char hostname[128];
	hostname[127] = '\0';
	gethostname(hostname,127);

	string modelfilename = "";
	string datafilename = "";
	string amplCommand = "/home/s0965328/research/ampl/ampl"; //default
	string outfilename = "";
	int degreeParallel = 1;
	bool debug = false;

	int rv = analyseOptions(argc, argv,modelfilename, datafilename, outfilename,amplCommand,degreeParallel,debug);

	if (rv)
	{
		return rv;
	}
	rv = createTmpDirIfNotPresent();
	if(rv)
	{
		return rv;
	}
	initGloablVariables(rank,size,modelfilename,datafilename,amplCommand,degreeParallel,debug);

	if(rank==0)
	{
		LOG("process started on host["<<hostname<<"]");
		if(GlobalVariables::debug)
		{
			cout << "======================================================" << endl;
			cout << "----------------- Call OOPS generator ----------------" << endl;
		}

		assert(size >= 2);
		assert (degreeParallel >= 1);


		Sml::writeCopyright(cout);
		ExpandedModelAbstract *em = Sml::instance()->sml_generate();
		if (!em)
		 return 1;

		cout << "Calling OOPS..." << endl;
		TIMER_START("SML_OOPS_DRIVER");
		SML_OOPS_driver(em);
		TIMER_STOP("SML_OOPS_DRIVER");
		if(outfilename!="") {
		  ofstream outfile(("../" + outfilename).c_str());
		  em->outputSolution(outfile);
		  outfile.close();
		  cout << "Solution written to file '" << outfilename << "'" << endl;
		}
		Sml::deleteInstance();
		delete em;
		TIMER_LIST;
		TIMER_RESET;
	}
	else
	{
		LOG("process started on host["<<hostname<<"]");
		slaveRun(rank);
	}
	MPI::Finalize();
	return 0;
}
